package com.clp.protocol.iec104.client.pipeline.state.data;

import com.clp.protocol.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.client.async.sendapdu.SendTotalCall101Res;
import com.clp.protocol.iec104.client.config.MasterDataConfig;
import com.clp.protocol.iec104.client.pipeline.MPipelineManager;
import com.clp.protocol.iec104.client.pipeline.state.MasterPromiseRegister;
import com.clp.protocol.core.pdu.nbytepdu.FailedToSendFrameException;
import com.clp.protocol.core.utils.AssertUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

@Slf4j
public class MTotalCall101DataHandler extends AbstractMDataStateHandler implements MasterPromiseRegister {

    private enum State {
        TOTAL_CALL_101,
        TOTAL_CALL_101_ACK,
        TOTAL_CALL_101_END;
    }

    @Getter
    private final int periodSeconds;

    private volatile State state;
    private volatile long lastTotalCall101Time;
    private volatile long lastTotalCall101AckTime;
    private volatile long lastTotalCall101EndTime;

    private final Vector<MasterPromise<SendTotalCall101Res>> promises = new Vector<>();

    public MTotalCall101DataHandler(MPipelineManager manager, MasterDataConfig cfg) {
        super(manager);
        this.periodSeconds = cfg.getTotalCall101PeriodSeconds();
    }

    @Override
    protected void resetState() {
        this.state = State.TOTAL_CALL_101_END;
        this.lastTotalCall101Time = System.currentTimeMillis();
        this.lastTotalCall101AckTime = System.currentTimeMillis();
        this.lastTotalCall101EndTime = System.currentTimeMillis();
        this.promises.clear();
    }

    @Override
    protected void afterResetState() {

    }

    @Override
    protected IAsdu updateStateBySend(IAsdu iAsdu) throws Exception {
        switch (iAsdu.getTypeTag()) {
            case C_CI_NA_1: // 总召唤101
                switch (iAsdu.getCot().getCause()) {
                    case COT_ACT: // 06:激活
                        if (state == State.TOTAL_CALL_101_END) {
                            state = State.TOTAL_CALL_101;
                            promises.forEach(promise -> promise.getRes().setSendSuccess(true));
                            lastTotalCall101Time = System.currentTimeMillis();
                        } else {
                            promises.forEach(promise -> {
                                promise.getRes().setSendSuccess(false).setRecvTotalCall101Ack(false).setRecvTotalCall101End(false);
                                promise.setFailure(new FailedToSendFrameException(iAsdu));
                            });
                            promises.clear();
                            log.warn("已经在进行总召唤101，放弃本次总召唤101命令！");
                            return null;
                        }
                        break;
                }
                break;
        }
        return iAsdu;
    }

    @Override
    protected IAsdu updateStateByRecv(IAsdu iAsdu) throws Exception {
        switch (iAsdu.getTypeTag()) {
            case C_CI_NA_1: // 总召唤101
                switch (iAsdu.getCot().getCause()) {
                    case COT_ACTCON: // 07:确认激活
                        if (state == State.TOTAL_CALL_101) {
                            promises.forEach(promise -> promise.getRes().setRecvTotalCall101Ack(true));
                            state = State.TOTAL_CALL_101_ACK;
                            lastTotalCall101AckTime = System.currentTimeMillis();
                        } else {
                            log.warn("接收到无效的总召唤确认报文！");
                        }
                        break;
                    case COT_ACTTERM: // 激活终止
                        if (state == State.TOTAL_CALL_101_ACK) {
                            promises.forEach(promise -> {
                                promise.getRes().setRecvTotalCall101End(true);
                                promise.setSuccess();
                            });
                            promises.clear();
                            state = State.TOTAL_CALL_101_END;
                            lastTotalCall101EndTime = System.currentTimeMillis();
                        } else {
                            log.warn("接收到无效的总召唤确认终止报文！");
                        }
                        break;
                }
                break;
        }
        return iAsdu;
    }

    @Override
    @Nullable
    protected ScheduledTask getScheduledTask() {
        return new ScheduledTask(0, 1, TimeUnit.SECONDS) {
            @Override
            public void run() {
                if (!inMaster().controlInfo().isInitCompleted()) {
                    return;
                }

                long nowTime = System.currentTimeMillis();
                switch (state) {
                    case TOTAL_CALL_101:
                        if (nowTime - lastTotalCall101Time > 1000 * 3) {
                            promises.forEach(promise -> {
                                promise.getRes().setRecvTotalCall101Ack(false).setRecvTotalCall101End(false);
                                promise.setFailure(new RuntimeException("发送总召唤101的3秒内没有收到确认报文，放弃本次总召唤！"));
                            });
                            promises.clear();
                            state = State.TOTAL_CALL_101_END;
                            lastTotalCall101EndTime = System.currentTimeMillis();
                            log.warn("发送总召唤101的3秒内没有收到确认报文，放弃本次总召唤！");
                        }
                        break;
                    case TOTAL_CALL_101_ACK:
                        if (nowTime - lastTotalCall101AckTime > 1000 * 3) {
                            promises.forEach(promise -> {
                                promise.getRes().setRecvTotalCall101End(false);
                                promise.setSuccess(); // TODO 默认为成功了
                            });
                            promises.clear();
                            state = State.TOTAL_CALL_101_END;
                            lastTotalCall101EndTime = System.currentTimeMillis();
                            log.warn("接收到总召唤101确认的3秒内没有收到召唤终止报文，默认本次召唤结束！");
                        }
                        break;
                    case TOTAL_CALL_101_END:
                        if (nowTime - lastTotalCall101EndTime > 1000L * periodSeconds) {
                            inMaster().getIAsduSender().sendTotalCall101();
                            log.info("周期性发送总召唤101");
                        }
                        break;
                }
            }
        };
    }

    @Override
    @SuppressWarnings("unchecked")
    public <V> void register(MasterPromise<V> sendPromise) {
        V res = sendPromise.getRes();
        AssertUtil.notNull(res);

        if (sendPromise.getRes() instanceof SendTotalCall101Res) {
            promises.add(((MasterPromise<SendTotalCall101Res>) sendPromise));
            return;
        }

        throw new IllegalArgumentException();
    }

    public boolean isDoingTotalCall101() {
        return state != State.TOTAL_CALL_101_END;
    }
}
